package org.osmdroid.views.overlay;

import java.util.AbstractList;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.concurrent.CopyOnWriteArrayList;

import org.osmdroid.api.IMapView;
import org.osmdroid.views.MapView;
import org.osmdroid.views.overlay.Overlay.Snappable;

import android.graphics.Canvas;
import android.graphics.Point;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;

public class OverlayManager extends AbstractList<Overlay> {

    private TilesOverlay mTilesOverlay;

    private final CopyOnWriteArrayList<Overlay> mOverlayList;

    public OverlayManager(final TilesOverlay tilesOverlay) {
        setTilesOverlay(tilesOverlay);
        mOverlayList = new CopyOnWriteArrayList<Overlay>();
    }

    @Override
    public Overlay get(final int pIndex) {
        return mOverlayList.get(pIndex);
    }

    @Override
    public int size() {
        return mOverlayList.size();
    }

    @Override
    public void add(final int pIndex, final Overlay pElement) {
        mOverlayList.add(pIndex, pElement);
    }

    @Override
    public Overlay remove(final int pIndex) {
        return mOverlayList.remove(pIndex);
    }

    @Override
    public Overlay set(final int pIndex, final Overlay pElement) {
        return mOverlayList.set(pIndex, pElement);
    }

    /**
     * Gets the optional TilesOverlay class.
     * 
     * @return the tilesOverlay
     */
    public TilesOverlay getTilesOverlay() {
        return mTilesOverlay;
    }

    /**
     * Sets the optional TilesOverlay class. If set, this overlay will be drawn
     * before all other overlays and will not be included in the editable list
     * of overlays and can't be cleared except by a subsequent call to
     * setTilesOverlay().
     * 
     * @param tilesOverlay
     *            the tilesOverlay to set
     */
    public void setTilesOverlay(final TilesOverlay tilesOverlay) {
        mTilesOverlay = tilesOverlay;
    }

    public Iterable<Overlay> overlaysReversed() {
        return new Iterable<Overlay>() {
            @Override
            public Iterator<Overlay> iterator() {
                final ListIterator<Overlay> i = mOverlayList
                        .listIterator(mOverlayList.size());

                return new Iterator<Overlay>() {
                    @Override
                    public boolean hasNext() {
                        return i.hasPrevious();
                    }

                    @Override
                    public Overlay next() {
                        return i.previous();
                    }

                    @Override
                    public void remove() {
                        i.remove();
                    }
                };
            }
        };
    }

    public void onDraw(final Canvas c, final MapView pMapView) {
        if ((mTilesOverlay != null) && mTilesOverlay.isEnabled()) {
            mTilesOverlay.draw(c, pMapView, true);
        }

        if ((mTilesOverlay != null) && mTilesOverlay.isEnabled()) {
            mTilesOverlay.draw(c, pMapView, false);
        }

        for (final Overlay overlay : mOverlayList) {
            if (overlay.isEnabled()) {
                overlay.draw(c, pMapView, true);
            }
        }

        for (final Overlay overlay : mOverlayList) {
            if (overlay.isEnabled()) {
                overlay.draw(c, pMapView, false);
            }
        }

    }

    public void onDetach(final MapView pMapView) {
        if (mTilesOverlay != null) {
            mTilesOverlay.onDetach(pMapView);
        }

        for (final Overlay overlay : this.overlaysReversed()) {
            overlay.onDetach(pMapView);
        }
    }

    public boolean onKeyDown(final int keyCode, final KeyEvent event,
            final MapView pMapView) {
        for (final Overlay overlay : this.overlaysReversed()) {
            if (overlay.onKeyDown(keyCode, event, pMapView)) {
                return true;
            }
        }

        return false;
    }

    public boolean onKeyUp(final int keyCode, final KeyEvent event,
            final MapView pMapView) {
        for (final Overlay overlay : this.overlaysReversed()) {
            if (overlay.onKeyUp(keyCode, event, pMapView)) {
                return true;
            }
        }

        return false;
    }

    public boolean onTouchEvent(final MotionEvent event, final MapView pMapView) {
        for (final Overlay overlay : this.overlaysReversed()) {
            if (overlay.onTouchEvent(event, pMapView)) {
                return true;
            }
        }

        return false;
    }

    public boolean onTrackballEvent(final MotionEvent event,
            final MapView pMapView) {
        for (final Overlay overlay : this.overlaysReversed()) {
            if (overlay.onTrackballEvent(event, pMapView)) {
                return true;
            }
        }

        return false;
    }

    public boolean onSnapToItem(final int x, final int y,
            final Point snapPoint, final IMapView pMapView) {
        for (final Overlay overlay : this.overlaysReversed()) {
            if (overlay instanceof Snappable) {
                if (((Snappable) overlay).onSnapToItem(x, y, snapPoint,
                        pMapView)) {
                    return true;
                }
            }
        }

        return false;
    }

    /** GestureDetector.OnDoubleTapListener **/

    public boolean onDoubleTap(final MotionEvent e, final MapView pMapView) {
        for (final Overlay overlay : this.overlaysReversed()) {
            if (overlay.onDoubleTap(e, pMapView)) {
                return true;
            }
        }

        return false;
    }

    public boolean onDoubleTapEvent(final MotionEvent e, final MapView pMapView) {
        for (final Overlay overlay : this.overlaysReversed()) {
            if (overlay.onDoubleTapEvent(e, pMapView)) {
                return true;
            }
        }

        return false;
    }

    public boolean onSingleTapConfirmed(final MotionEvent e,
            final MapView pMapView) {
        for (final Overlay overlay : this.overlaysReversed()) {
            if (overlay.onSingleTapConfirmed(e, pMapView)) {
                return true;
            }
        }

        return false;
    }

    /** OnGestureListener **/

    public boolean onDown(final MotionEvent pEvent, final MapView pMapView) {
        for (final Overlay overlay : this.overlaysReversed()) {
            if (overlay.onDown(pEvent, pMapView)) {
                return true;
            }
        }

        return false;
    }

    public boolean onFling(final MotionEvent pEvent1,
            final MotionEvent pEvent2, final float pVelocityX,
            final float pVelocityY, final MapView pMapView) {
        for (final Overlay overlay : this.overlaysReversed()) {
            if (overlay.onFling(pEvent1, pEvent2, pVelocityX, pVelocityY,
                    pMapView)) {
                return true;
            }
        }

        return false;
    }

    public boolean onLongPress(final MotionEvent pEvent, final MapView pMapView) {
        for (final Overlay overlay : this.overlaysReversed()) {
            if (overlay.onLongPress(pEvent, pMapView)) {
                return true;
            }
        }

        return false;
    }

    public boolean onScroll(final MotionEvent pEvent1,
            final MotionEvent pEvent2, final float pDistanceX,
            final float pDistanceY, final MapView pMapView) {
        for (final Overlay overlay : this.overlaysReversed()) {
            if (overlay.onScroll(pEvent1, pEvent2, pDistanceX, pDistanceY,
                    pMapView)) {
                return true;
            }
        }

        return false;
    }

    public void onShowPress(final MotionEvent pEvent, final MapView pMapView) {
        for (final Overlay overlay : this.overlaysReversed()) {
            overlay.onShowPress(pEvent, pMapView);
        }
    }

    public boolean onSingleTapUp(final MotionEvent pEvent,
            final MapView pMapView) {
        for (final Overlay overlay : this.overlaysReversed()) {
            if (overlay.onSingleTapUp(pEvent, pMapView)) {
                return true;
            }
        }

        return false;
    }

    // ** Options Menu **//

    public void setOptionsMenusEnabled(final boolean pEnabled) {
        for (final Overlay overlay : mOverlayList) {
            if ((overlay instanceof IOverlayMenuProvider)
                    && ((IOverlayMenuProvider) overlay).isOptionsMenuEnabled()) {
                ((IOverlayMenuProvider) overlay)
                        .setOptionsMenuEnabled(pEnabled);
            }
        }
    }

    public boolean onCreateOptionsMenu(final Menu pMenu,
            final int menuIdOffset, final MapView mapView) {
        boolean result = true;
        for (final Overlay overlay : this.overlaysReversed()) {
            if ((overlay instanceof IOverlayMenuProvider)
                    && ((IOverlayMenuProvider) overlay).isOptionsMenuEnabled()) {
                result &= ((IOverlayMenuProvider) overlay).onCreateOptionsMenu(
                        pMenu, menuIdOffset, mapView);
            }
        }

        if ((mTilesOverlay != null)
                && (mTilesOverlay instanceof IOverlayMenuProvider)
                && ((IOverlayMenuProvider) mTilesOverlay)
                        .isOptionsMenuEnabled()) {
            result &= mTilesOverlay.onCreateOptionsMenu(pMenu, menuIdOffset,
                    mapView);
        }

        return result;
    }

    public boolean onPrepareOptionsMenu(final Menu pMenu,
            final int menuIdOffset, final MapView mapView) {
        for (final Overlay overlay : this.overlaysReversed()) {
            if ((overlay instanceof IOverlayMenuProvider)
                    && ((IOverlayMenuProvider) overlay).isOptionsMenuEnabled()) {
                ((IOverlayMenuProvider) overlay).onPrepareOptionsMenu(pMenu,
                        menuIdOffset, mapView);
            }
        }

        if ((mTilesOverlay != null)
                && (mTilesOverlay instanceof IOverlayMenuProvider)
                && ((IOverlayMenuProvider) mTilesOverlay)
                        .isOptionsMenuEnabled()) {
            mTilesOverlay.onPrepareOptionsMenu(pMenu, menuIdOffset, mapView);
        }

        return true;
    }

    public boolean onOptionsItemSelected(final MenuItem item,
            final int menuIdOffset, final MapView mapView) {
        for (final Overlay overlay : this.overlaysReversed()) {
            if ((overlay instanceof IOverlayMenuProvider)
                    && ((IOverlayMenuProvider) overlay).isOptionsMenuEnabled()
                    && ((IOverlayMenuProvider) overlay).onOptionsItemSelected(
                            item, menuIdOffset, mapView)) {
                return true;
            }
        }

        if ((mTilesOverlay != null)
                && (mTilesOverlay instanceof IOverlayMenuProvider)
                && ((IOverlayMenuProvider) mTilesOverlay)
                        .isOptionsMenuEnabled()
                && ((IOverlayMenuProvider) mTilesOverlay)
                        .onOptionsItemSelected(item, menuIdOffset, mapView)) {
            return true;
        }

        return false;
    }
}
